package cn.zmwh.im.server.netty.processor;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.zmwh.im.common.enums.IMTerminalType;
import cn.zmwh.im.common.pojo.dto.IMRecvInfo;
import cn.zmwh.im.common.pojo.dto.IMUserInfo;
import cn.zmwh.im.common.pojo.dto.transfer.MessageGroup;
import cn.zmwh.im.server.config.HintManager;
import cn.zmwh.im.server.constant.BsKImConstant;
import cn.zmwh.im.server.constant.IMCmdConstants;
import cn.zmwh.im.server.constant.IMRedisKey;
import cn.zmwh.im.server.modules.message.pojo.entity.ImGroupMessage;
import cn.zmwh.im.server.modules.message.service.ImGroupMessageService;
import cn.zmwh.im.server.modules.ums.pojo.entity.ImGroup;
import cn.zmwh.im.server.modules.ums.pojo.entity.ImGroupMember;
import cn.zmwh.im.server.modules.ums.service.ImGroupMemberService;
import cn.zmwh.im.server.modules.ums.service.ImGroupService;
import cn.zmwh.im.server.mq.dto.IMInfo;
import cn.zmwh.im.server.netty.UserChannelMap;
import cn.zmwh.im.server.utils.IdGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Component("groupProcessor")
public class MessageProcessorGroup implements ImMessageProcessor {

    private final RedisTemplate redisTemplate;
    private final ImGroupService imGroupService;
    private final ImGroupMemberService imGroupMemberService;
    private final ImGroupMessageService imGroupMessageService;

    @Override
    public IMInfo read(ChannelHandlerContext ctx, IMInfo sendInfo) {
        Channel channel = ctx.channel();
        AttributeKey<Long> userIdAttr = AttributeKey.valueOf(BsKImConstant.CHANNEL_ATTR_KEY.USER_ID);
        Long sendId = channel.attr(userIdAttr).get();
        AttributeKey<String> terminalAttr = AttributeKey.valueOf(BsKImConstant.CHANNEL_ATTR_KEY.TERMINAL_TYPE);
        String terminal = channel.attr(terminalAttr).get();
        String sendKey = StrUtil.join(":", IMRedisKey.GAIN_SERVER_ID, sendId, terminal);

        Object data = sendInfo.getData();
        MessageGroup messageGroup = JSONUtil.toBean(data.toString(), MessageGroup.class);
        IMInfo<Object> result = new IMInfo<>();
        result.setCmd(IMCmdConstants.GROUP_MESSAGE);

        ImGroup byId = imGroupService.getById(messageGroup.getGroupId());
        if (byId == null) {
            messageGroup.setStatus(-1);
            messageGroup.setMsg("群聊不存在");
            result.setData(messageGroup);
            return result;
        }
        List<ImGroupMember> list = imGroupMemberService.list();
        if (CollUtil.isEmpty(list)) {
            messageGroup.setStatus(-1);
            messageGroup.setMsg("群聊不存在用户");
            result.setData(messageGroup);
            return result;
        }
        List<ImGroupMember> collect = list.stream().filter(item -> item.getUid().equals(sendId)).collect(Collectors.toList());
        if (CollUtil.isEmpty(collect)) {
            messageGroup.setStatus(-1);
            messageGroup.setMsg("您已经退出该群聊！");
            result.setData(messageGroup);
            return result;
        }
//        // 群聊成员列表
        String messageId = IdGenerator.nextIdStr();
        messageGroup.setMessageId(messageId);
        messageGroup.setSendId(sendId);
        messageGroup.setSendTime(new Date());

        //todo 入库成功则认为投递成功

        IMRecvInfo imRecvInfo = new IMRecvInfo();
        imRecvInfo.setCmd(IMCmdConstants.GROUP_MESSAGE);
        imRecvInfo.setData(messageGroup);
        imRecvInfo.setMessageId(messageId);
        imRecvInfo.setSender(new IMUserInfo(sendId, terminal));

        Map<String, IMUserInfo> receiverMap = new HashMap<>();
        for (ImGroupMember item : list) {
            Long uid = item.getUid();
            IMTerminalType[] values = IMTerminalType.values();
            for (IMTerminalType value : values) {
                String terminalV = value.terminal();
                String key = StrUtil.join(":", IMRedisKey.GAIN_SERVER_ID, uid, terminalV);
                if (sendKey.equals(key)) {
                    continue;
                }
                receiverMap.put(key, new IMUserInfo(uid, terminalV));
            }
        }
        if (MapUtil.isNotEmpty(receiverMap)) {
            // 批量拉取
            List<Object> serverIds = redisTemplate.opsForValue().multiGet(receiverMap.keySet());
            Map<String, List<IMUserInfo>> map = new HashMap<>();
            int index = 0;
            for (Map.Entry<String, IMUserInfo> entry : receiverMap.entrySet()) {
                Object serverId = serverIds.get(index++);//index++后置递增， ++index前置递增
                if (serverId == null) {
                    continue;
                }
                IMUserInfo value = entry.getValue();
                String serverIdStr = serverId.toString();
                map.computeIfAbsent(serverIdStr, k -> new ArrayList<>()).add(value);//根据服务分组
            }

            Set<Map.Entry<String, List<IMUserInfo>>> entries = map.entrySet();
            for (Map.Entry<String, List<IMUserInfo>> entry : entries) {
                String key = entry.getKey();
                List<IMUserInfo> value = entry.getValue();
                imRecvInfo.setReceivers(value);
                String queueKey = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, key);
                redisTemplate.opsForList().rightPush(queueKey, imRecvInfo);
            }
        }
        messageGroup.setStatus(1);

        ImGroupMessage imGroupMessage = new ImGroupMessage();
        BeanUtil.copyProperties(messageGroup, imGroupMessage);
        List<Long> atUserIds = messageGroup.getAtUserIds();
        if (CollUtil.isNotEmpty(atUserIds)) {
            imGroupMessage.setAtUserIds(JSONUtil.toJsonStr(atUserIds));
        }

        //保存消息
        try (HintManager hintManager = HintManager.getInstance()) {
            Integer messageDb = byId.getMessageDb();
            Integer messageTable = byId.getMessageTable();
            hintManager.addDbValue(BsKImConstant.DbName.IM_MESSAGE, messageDb);
            hintManager.addTableValue(messageTable);
            imGroupMessageService.save(imGroupMessage);
        }
        result.setMessageId(messageId);
        return result;
    }

    @Override
    public void send(IMRecvInfo recvInfo) {
        IMUserInfo sender = recvInfo.getSender();
        String messageId = recvInfo.getMessageId();
        List<IMUserInfo> receivers = recvInfo.getReceivers();
        log.info("接收到群消息，发送者:{},接收用户数量:{}，内容:{}", sender.getId(), receivers.size(), recvInfo.getData());
        for (IMUserInfo receiver : receivers) {
            try {
                Channel channel = UserChannelMap.getChannel(receiver.getId(), receiver.getTerminal());
                if (channel == null) {
                    log.error("未找到channel,发送者:{},接收id:{}，内容:{}", sender.getId(), receiver.getId(), recvInfo.getData());
                    continue;
                }
                // 推送消息到用户
                IMInfo<Object> sendInfo = new IMInfo<>();
                sendInfo.setMessageId(messageId);
                sendInfo.setWantAck(false);
                sendInfo.setCmd(IMCmdConstants.GROUP_MESSAGE);
                sendInfo.setData(recvInfo.getData());
                channel.writeAndFlush(sendInfo);
            } catch (Exception e) {
                // 消息发送失败确认
                log.error("发送消息异常,发送者:{},接收id:{}，内容:{}", sender.getId(), receiver.getId(), recvInfo.getData(), e);
            }
        }
        IMUserInfo receiver = receivers.get(0);
        Object data = recvInfo.getData();
        MessageGroup messageGroup = BeanUtil.toBean(data, MessageGroup.class);
        Long groupId = messageGroup.getGroupId();
        ImGroup byId = imGroupService.getById(groupId);
        if (byId == null) {
            log.error("群不存在,发送者:{},接收id:{}，内容:{}", sender.getId(), receiver.getId(), recvInfo.getData());
            return;
        }
        Long sendId = messageGroup.getSendId();
        Integer messageDb = byId.getMessageDb();
        Integer messageTable = byId.getMessageTable();
        try (HintManager hintManager = HintManager.getInstance()) {
            hintManager.addDbValue(BsKImConstant.DbName.IM_MESSAGE, messageDb);
            hintManager.addTableValue(messageTable);
            ImGroupMessage one = imGroupMessageService.getOne(Wrappers.<ImGroupMessage>lambdaQuery()
                    .eq(ImGroupMessage::getSendId, sendId)
                    .eq(ImGroupMessage::getMessageId, messageId));
            one.setStatus(15);
            imGroupMessageService.updateById(one);
        }
    }

    @Override
    public void ack(Long uid, String messageId, Object data) {
        MessageGroup messageGroup = BeanUtil.toBean(data, MessageGroup.class);
        Long groupId = messageGroup.getGroupId();
        ImGroup byId = imGroupService.getById(groupId);
        if (byId == null) {
            return;
        }
        Long sendId = messageGroup.getSendId();

        Integer messageDb = byId.getMessageDb();
        Integer messageTable = byId.getMessageTable();

        try (HintManager hintManager = HintManager.getInstance()) {
            hintManager.addDbValue(BsKImConstant.DbName.IM_MESSAGE, messageDb);
            hintManager.addTableValue(messageTable);
            ImGroupMessage one = imGroupMessageService.getOne(Wrappers.<ImGroupMessage>lambdaQuery()
                    .eq(ImGroupMessage::getSendId, sendId)
                    .eq(ImGroupMessage::getMessageId, messageId));
            int i = one.getReadCount() + 1;
            one.setReadCount(i);
            imGroupMessageService.updateById(one);
        }
        //todo 更新uid其他终端的已读状态，防止出现重复标记已读情况
    }
}
